/*-------------------------------------------------------------------------
 *
 * makefuncs.c
 *	  各种节点的创建函数。这里的函数用于最常创建的节点。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/nodes/makefuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "utils/lsyscache.h"


/*
 * makeA_Expr -
 *		制作一个 A_Expr 节点
 */
A_Expr *
makeA_Expr(A_Expr_Kind fc_kind, List *fc_name,
		   Node *fc_lexpr, Node *fc_rexpr, int fc_location)
{
	A_Expr	   *fc_a = makeNode(A_Expr);

	fc_a->kind = fc_kind;
	fc_a->name = fc_name;
	fc_a->lexpr = fc_lexpr;
	fc_a->rexpr = fc_rexpr;
	fc_a->location = fc_location;
	return fc_a;
}

/*
 * makeSimpleA_Expr -
 *		如上，给定一个简单（未限定）的操作符名称
 */
A_Expr *
makeSimpleA_Expr(A_Expr_Kind fc_kind, char *fc_name,
				 Node *fc_lexpr, Node *fc_rexpr, int fc_location)
{
	A_Expr	   *fc_a = makeNode(A_Expr);

	fc_a->kind = fc_kind;
	fc_a->name = list_make1(makeString((char *) fc_name));
	fc_a->lexpr = fc_lexpr;
	fc_a->rexpr = fc_rexpr;
	fc_a->location = fc_location;
	return fc_a;
}

/*
 * makeVar -
 *	  创建一个 Var 节点
 */
Var * makeVar(int fc_varno,
		AttrNumber fc_varattno,
		Oid fc_vartype,
		int32 fc_vartypmod,
		Oid fc_varcollid,
		Index fc_varlevelsup)
{
	Var		   *fc_var = makeNode(Var);

	fc_var->varno = fc_varno;
	fc_var->varattno = fc_varattno;
	fc_var->vartype = fc_vartype;
	fc_var->vartypmod = fc_vartypmod;
	fc_var->varcollid = fc_varcollid;
	fc_var->varlevelsup = fc_varlevelsup;

	/*
	 * 只有少数调用者需要创建 varnosyn/varattnosyn
	 * 与 varno/varattno 不同的 Var 节点。我们没有为它们提供单独的参数，
	 * 而只是将它们初始化为给定的 varno/varattno。这
	 * 减少了大多数调用者的代码杂乱和错误的机会。
	 */
	fc_var->varnosyn = (Index) fc_varno;
	fc_var->varattnosyn = fc_varattno;

	/* 同样，我们在这里将位置设置为“未知” */
	fc_var->location = -1;

	return fc_var;
}

/*
 * makeVarFromTargetEntry -
 *		方便函数，从 TargetEntry 创建同级 Var 节点
 */
Var * makeVarFromTargetEntry(int fc_varno,
					   TargetEntry *fc_tle)
{
	return makeVar(fc_varno,
				   fc_tle->resno,
				   exprType((Node *) fc_tle->expr),
				   exprTypmod((Node *) fc_tle->expr),
				   exprCollation((Node *) fc_tle->expr),
				   0);
}

/*
 * makeWholeRowVar -
 *	  创建一个表征指定 RTE 的整行 Var 节点
 *
 * 整行引用是一个 Var，其 varno 设置为正确的范围
 * 表条目，varattno == 0 用于表示它引用整个
 * 元组。（这里使用零是不干净的，因为它很容易与错误情况混淆，
 * 但现在改变它不值得。）vartype 表示
 * 一个行类型；可以是一个命名的复合类型，或者是一个基于命名
 * 复合类型的域（仅当 RTE 是返回那类型的函数时才可能），
 * 或 RECORD。此函数封装了确定
 * 正确行类型 OID 的逻辑。
 *
 * 如果 allowScalar 为真，则当 RTE 是一个返回
 * 非复合结果类型的单个函数时，我们生成一个正常的 Var 直接引用
 * 函数的结果，而不是整行表示法可能暗示的单列复合
 * 值。
 */
Var * makeWholeRowVar(RangeTblEntry *fc_rte,
				int fc_varno,
				Index fc_varlevelsup,
				bool fc_allowScalar)
{
	Var		   *fc_result;
	Oid			fc_toid;
	Node	   *fc_fexpr;

	switch (fc_rte->rtekind)
	{
		case RTE_RELATION:
			/* relation: 行类型是一个命名的复合类型 */
			fc_toid = get_rel_type_id(fc_rte->relid);
			if (!OidIsValid(fc_toid))
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("relation \"%s\" does not have a composite type",
								get_rel_name(fc_rte->relid))));
			fc_result = makeVar(fc_varno,
							 InvalidAttrNumber,
							 fc_toid,
							 -1,
							 InvalidOid,
							 fc_varlevelsup);
			break;

		case RTE_FUNCTION:

			/*
			 * 如果有多个函数，或者请求了基数，
			 * 则强制返回 RECORD 结果，因为这
			 * 确实涉及多个列，并且无法是已知的命名类型。
			 */
			if (fc_rte->funcordinality || list_length(fc_rte->functions) != 1)
			{
				/* 总是生成一个匿名 RECORD 结果 */
				fc_result = makeVar(fc_varno,
								 InvalidAttrNumber,
								 RECORDOID,
								 -1,
								 InvalidOid,
								 fc_varlevelsup);
				break;
			}

			fc_fexpr = ((RangeTblFunction *) linitial(fc_rte->functions))->funcexpr;
			fc_toid = exprType(fc_fexpr);
			if (type_is_rowtype(fc_toid))
			{
				/* func 返回复合；同 relation 情况 */
				fc_result = makeVar(fc_varno,
								 InvalidAttrNumber,
								 fc_toid,
								 -1,
								 InvalidOid,
								 fc_varlevelsup);
			}
			else if (fc_allowScalar)
			{
				/* func 返回标量；直接返回其输出 */
				fc_result = makeVar(fc_varno,
								 1,
								 fc_toid,
								 -1,
								 exprCollation(fc_fexpr),
								 fc_varlevelsup);
			}
			else
			{
				/* func 返回标量，但我们想要复合结果 */
				fc_result = makeVar(fc_varno,
								 InvalidAttrNumber,
								 RECORDOID,
								 -1,
								 InvalidOid,
								 fc_varlevelsup);
			}
			break;

		default:

			/*
			 * RTE 是一个连接、子选择、tablefunc 或 VALUES。我们将
			 * 其表示为一个 RECORD 类型的整行 Var。（请注意，在大多数
			 * 情况下，Var 将在规划期间扩展为 RowExpr，
			 * 但这不是我们这里关注的问题。）
			 */
			fc_result = makeVar(fc_varno,
							 InvalidAttrNumber,
							 RECORDOID,
							 -1,
							 InvalidOid,
							 fc_varlevelsup);
			break;
	}

	return fc_result;
}

/*
 * makeTargetEntry -
 *	  创建一个 TargetEntry 节点
 */
TargetEntry * makeTargetEntry(Expr *fc_expr,
				AttrNumber fc_resno,
				char *fc_resname,
				bool fc_resjunk)
{
	TargetEntry *fc_tle = makeNode(TargetEntry);

	fc_tle->expr = fc_expr;
	fc_tle->resno = fc_resno;
	fc_tle->resname = fc_resname;

	/*
	 * 我们总是将这些字段设置为 0。如果调用者想要改变它们，他
	 * 必须显式地这样做。很少有调用者这样做，因此省略这些
	 * 参数减少了出错的机会。
	 */
	fc_tle->ressortgroupref = 0;
	fc_tle->resorigtbl = InvalidOid;
	fc_tle->resorigcol = 0;

	fc_tle->resjunk = fc_resjunk;

	return fc_tle;
}

/*
 * flatCopyTargetEntry -
 *	  复制一个 TargetEntry，但不复制子结构
 *
 * 这常用于我们只是想修改 resno 或替换
 * 新表达式时。
 */
TargetEntry * flatCopyTargetEntry(TargetEntry *fc_src_tle)
{
	TargetEntry *fc_tle = makeNode(TargetEntry);

	Assert(IsA(fc_src_tle, TargetEntry));
	memcpy(fc_tle, fc_src_tle, sizeof(TargetEntry));
	return fc_tle;
}

/*
 * makeFromExpr -
 *	  创建一个 FromExpr 节点
 */
FromExpr *
makeFromExpr(List *fc_fromlist, Node *fc_quals)
{
	FromExpr   *fc_f = makeNode(FromExpr);

	fc_f->fromlist = fc_fromlist;
	fc_f->quals = fc_quals;
	return fc_f;
}

/*
 * makeConst -
 *	  创建一个 Const 节点
 */
Const * makeConst(Oid fc_consttype,
		  int32 fc_consttypmod,
		  Oid fc_constcollid,
		  int fc_constlen,
		  Datum fc_constvalue,
		  bool fc_constisnull,
		  bool fc_constbyval)
{
	Const	   *fc_cnst = makeNode(Const);

	/*
	 * 如果是 varlena 值，将其强制为非扩展（非 toasted）
	 * 格式；这避免了对外部值的任何可能依赖，并
	 * 改善了表示的一致性，这对 equal() 至关重要。
	 */
	if (!fc_constisnull && fc_constlen == -1)
		fc_constvalue = PointerGetDatum(PG_DETOAST_DATUM(fc_constvalue));

	fc_cnst->consttype = fc_consttype;
	fc_cnst->consttypmod = fc_consttypmod;
	fc_cnst->constcollid = fc_constcollid;
	fc_cnst->constlen = fc_constlen;
	fc_cnst->constvalue = fc_constvalue;
	fc_cnst->constisnull = fc_constisnull;
	fc_cnst->constbyval = fc_constbyval;
	fc_cnst->location = -1;		/* "unknown" */

	return fc_cnst;
}

/*
 * makeNullConst -
 *	  创建一个代表指定类型/typmod 的 NULL 的 Const 节点
 *
 * 这是一个方便的例程，仅保存类型的
 * 存储属性查找。
 */
Const * makeNullConst(Oid fc_consttype, int32 fc_consttypmod, Oid fc_constcollid)
{
	int16		fc_typLen;
	bool		fc_typByVal;

	get_typlenbyval(fc_consttype, &fc_typLen, &fc_typByVal);
	return makeConst(fc_consttype,
					 fc_consttypmod,
					 fc_constcollid,
					 (int) fc_typLen,
					 (Datum) 0,
					 true,
					 fc_typByVal);
}

/*
 * makeBoolConst -
 *	  创建一个表示布尔值（也可以为 NULL）的 Const 节点
 */
Node * makeBoolConst(bool fc_value, bool fc_isnull)
{
	/* 注意 pg_type.h 硬编码的布尔值大小为 1 ... 复制它 */
	return (Node *) makeConst(BOOLOID, -1, InvalidOid, 1,
							  BoolGetDatum(fc_value), fc_isnull, true);
}

/*
 * makeBoolExpr -
 *	  创建一个 BoolExpr 节点
 */
Expr * makeBoolExpr(BoolExprType fc_boolop, List *fc_args, int fc_location)
{
	BoolExpr   *fc_b = makeNode(BoolExpr);

	fc_b->boolop = fc_boolop;
	fc_b->args = fc_args;
	fc_b->location = fc_location;

	return (Expr *) fc_b;
}

/*
 * makeAlias -
 *	  创建一个 Alias 节点
 *
 * 注意：给定的名称被复制，但 colnames 列表（如果有的话）则没有。
 */
Alias *
makeAlias(const char *fc_aliasname, List *fc_colnames)
{
	Alias	   *fc_a = makeNode(Alias);

	fc_a->aliasname = pstrdup(fc_aliasname);
	fc_a->colnames = fc_colnames;

	return fc_a;
}

/*
 * makeRelabelType -
 *	  创建一个 RelabelType 节点
 */
RelabelType *
makeRelabelType(Expr *fc_arg, Oid fc_rtype, int32 fc_rtypmod, Oid fc_rcollid,
				CoercionForm fc_rformat)
{
	RelabelType *fc_r = makeNode(RelabelType);

	fc_r->arg = fc_arg;
	fc_r->resulttype = fc_rtype;
	fc_r->resulttypmod = fc_rtypmod;
	fc_r->resultcollid = fc_rcollid;
	fc_r->relabelformat = fc_rformat;
	fc_r->location = -1;

	return fc_r;
}

/*
 * makeRangeVar -
 *	  创建一个 RangeVar 节点（简化案例）
 */
RangeVar *
makeRangeVar(char *fc_schemaname, char *fc_relname, int fc_location)
{
	RangeVar   *fc_r = makeNode(RangeVar);

	fc_r->catalogname = NULL;
	fc_r->schemaname = fc_schemaname;
	fc_r->relname = fc_relname;
	fc_r->inh = true;
	fc_r->relpersistence = RELPERSISTENCE_PERMANENT;
	fc_r->alias = NULL;
	fc_r->location = fc_location;

	return fc_r;
}

/*
 * makeTypeName -
 *	为未限定的名称构建一个 TypeName 节点。
 *
 * typmod 是默认的，但可以稍后由调用者更改。
 */
TypeName * makeTypeName(char *fc_typnam)
{
	return makeTypeNameFromNameList(list_make1(makeString(fc_typnam)));
}

/*
 * makeTypeNameFromNameList -
 *	为表示合格名称的字符串列表构建一个 TypeName 节点。
 *
 * typmod 是默认的，但可以稍后由调用者更改。
 */
TypeName * makeTypeNameFromNameList(List *fc_names)
{
	TypeName   *fc_n = makeNode(TypeName);

	fc_n->names = fc_names;
	fc_n->typmods = NIL;
	fc_n->typemod = -1;
	fc_n->location = -1;
	return fc_n;
}

/*
 * makeTypeNameFromOid -
 *	构建一个 TypeName 节点以表示已经通过 OID/typmod 知道的类型。
 */
TypeName * makeTypeNameFromOid(Oid fc_typeOid, int32 fc_typmod)
{
	TypeName   *fc_n = makeNode(TypeName);

	fc_n->typeOid = fc_typeOid;
	fc_n->typemod = fc_typmod;
	fc_n->location = -1;
	return fc_n;
}

/*
 * makeColumnDef -
 *	构建一个 ColumnDef 节点以表示简单的列定义。
 *
 * 类型和排序通过 OID 指定。
 * 其他属性都是基本的，以便于开始。
 */
ColumnDef *
makeColumnDef(const char *fc_colname, Oid fc_typeOid, int32 fc_typmod, Oid fc_collOid)
{
	ColumnDef  *fc_n = makeNode(ColumnDef);

	fc_n->colname = pstrdup(fc_colname);
	fc_n->typeName = makeTypeNameFromOid(fc_typeOid, fc_typmod);
	fc_n->inhcount = 0;
	fc_n->is_local = true;
	fc_n->is_not_null = false;
	fc_n->is_from_type = false;
	fc_n->storage = 0;
	fc_n->raw_default = NULL;
	fc_n->cooked_default = NULL;
	fc_n->collClause = NULL;
	fc_n->collOid = fc_collOid;
	fc_n->constraints = NIL;
	fc_n->fdwoptions = NIL;
	fc_n->location = -1;

	return fc_n;
}

/*
 * makeFuncExpr -
 *	构建一个表示函数调用的表达式树。
 *
 * 参数表达式必须已被转换。
 */
FuncExpr *
makeFuncExpr(Oid fc_funcid, Oid fc_rettype, List *fc_args,
			 Oid fc_funccollid, Oid fc_inputcollid, CoercionForm fc_fformat)
{
	FuncExpr   *fc_funcexpr;

	fc_funcexpr = makeNode(FuncExpr);
	fc_funcexpr->funcid = fc_funcid;
	fc_funcexpr->funcresulttype = fc_rettype;
	fc_funcexpr->funcretset = false;	
/* 这里仅允许的案例 */
	fc_funcexpr->funcvariadic = false; 
/* 这里仅允许的案例 */
	fc_funcexpr->funcformat = fc_fformat;
	fc_funcexpr->funccollid = fc_funccollid;
	fc_funcexpr->inputcollid = fc_inputcollid;
	fc_funcexpr->args = fc_args;
	fc_funcexpr->location = -1;

	return fc_funcexpr;
}

/*
 * makeDefElem -
 *	构建一个 DefElem 节点
 *
 * 这足以应对 "典型" 情形，即不带限定的选项名称
 * 并且没有特殊操作。
 */
DefElem *
makeDefElem(char *fc_name, Node *fc_arg, int fc_location)
{
	DefElem    *fc_res = makeNode(DefElem);

	fc_res->defnamespace = NULL;
	fc_res->defname = fc_name;
	fc_res->arg = fc_arg;
	fc_res->defaction = DEFELEM_UNSPEC;
	fc_res->location = fc_location;

	return fc_res;
}

/*
 * makeDefElemExtended -
 *	构建一个 DefElem 节点，并可指定所有字段
 */
DefElem *
makeDefElemExtended(char *fc_nameSpace, char *fc_name, Node *fc_arg,
					DefElemAction fc_defaction, int fc_location)
{
	DefElem    *fc_res = makeNode(DefElem);

	fc_res->defnamespace = fc_nameSpace;
	fc_res->defname = fc_name;
	fc_res->arg = fc_arg;
	fc_res->defaction = fc_defaction;
	fc_res->location = fc_location;

	return fc_res;
}

/*
 * makeFuncCall -
 *
 * 用每个调用者必须提供的信息初始化一个 FuncCall 结构体。
 * 任何非默认参数必须由调用者插入。
 */
FuncCall *
makeFuncCall(List *fc_name, List *fc_args, CoercionForm fc_funcformat, int fc_location)
{
	FuncCall   *fc_n = makeNode(FuncCall);

	fc_n->funcname = fc_name;
	fc_n->args = fc_args;
	fc_n->agg_order = NIL;
	fc_n->agg_filter = NULL;
	fc_n->over = NULL;
	fc_n->agg_within_group = false;
	fc_n->agg_star = false;
	fc_n->agg_distinct = false;
	fc_n->func_variadic = false;
	fc_n->funcformat = fc_funcformat;
	fc_n->location = fc_location;
	return fc_n;
}

/*
 * make_opclause
 *	  给定其运算符信息、左操作数和右操作数（传 NULL 创建单操作数子句），
 *	  以及排序信息，创建一个操作符子句。
 */
Expr * make_opclause(Oid fc_opno, Oid fc_opresulttype, bool fc_opretset,
			  Expr *fc_leftop, Expr *fc_rightop,
			  Oid fc_opcollid, Oid fc_inputcollid)
{
	OpExpr	   *fc_expr = makeNode(OpExpr);

	fc_expr->opno = fc_opno;
	fc_expr->opfuncid = InvalidOid;
	fc_expr->opresulttype = fc_opresulttype;
	fc_expr->opretset = fc_opretset;
	fc_expr->opcollid = fc_opcollid;
	fc_expr->inputcollid = fc_inputcollid;
	if (fc_rightop)
		fc_expr->args = list_make2(fc_leftop, fc_rightop);
	else
		fc_expr->args = list_make1(fc_leftop);
	fc_expr->location = -1;
	return (Expr *) fc_expr;
}

/*
 * make_andclause
 *
 * 给定其子子句列表，创建一个 'and' 子句。
 */
Expr * make_andclause(List *fc_andclauses)
{
	BoolExpr   *fc_expr = makeNode(BoolExpr);

	fc_expr->boolop = AND_EXPR;
	fc_expr->args = fc_andclauses;
	fc_expr->location = -1;
	return (Expr *) fc_expr;
}

/*
 * make_orclause
 *
 * 给定其子子句列表，创建一个 'or' 子句。
 */
Expr * make_orclause(List *fc_orclauses)
{
	BoolExpr   *fc_expr = makeNode(BoolExpr);

	fc_expr->boolop = OR_EXPR;
	fc_expr->args = fc_orclauses;
	fc_expr->location = -1;
	return (Expr *) fc_expr;
}

/*
 * make_notclause
 *
 * 给定要取反的表达式，创建一个 'not' 子句。
 */
Expr * make_notclause(Expr *fc_notclause)
{
	BoolExpr   *fc_expr = makeNode(BoolExpr);

	fc_expr->boolop = NOT_EXPR;
	fc_expr->args = list_make1(fc_notclause);
	fc_expr->location = -1;
	return (Expr *) fc_expr;
}

/*
 * make_and_qual
 *
 * make_andclause 的变体，用于将两个条件合并为 AND 条件。
 * 条件具有 NULL 节点树被解释为 'true' 的特性。
 *
 * 注意：这不尝试保持 AND/OR 的平坦性；因此不应用于已经通过 prepqual.c 处理的条件。
 */
Node * make_and_qual(Node *fc_qual1, Node *fc_qual2)
{
	if (fc_qual1 == NULL)
		return fc_qual2;
	if (fc_qual2 == NULL)
		return fc_qual1;
	return (Node *) make_andclause(list_make2(fc_qual1, fc_qual2));
}

/*
 * 规划器和执行器通常将资格表达式表示为带有隐式 AND 语义的布尔表达式列表。
 *
 * 这些函数在 AND 语义表达式列表和布尔表达式的普通表示之间进行转换。
 *
 * 注意，空列表被视为等同于 TRUE。
 */
Expr * make_ands_explicit(List *fc_andclauses)
{
	if (fc_andclauses == NIL)
		return (Expr *) makeBoolConst(true, false);
	else if (list_length(fc_andclauses) == 1)
		return (Expr *) linitial(fc_andclauses);
	else
		return make_andclause(fc_andclauses);
}

List * make_ands_implicit(Expr *fc_clause)
{
	/*
	 * 注意：因为解析器在没有 WHERE 子句的查询中将 qual 字段设置为 NULL，
	 * 我们必须将 NULL 输入子句视为 TRUE，即使人们可能更合理地认为它为 FALSE。
	 */
	if (fc_clause == NULL)
		return NIL;				/* NULL -> NIL 列 == TRUE */
	else if (is_andclause(fc_clause))
		return ((BoolExpr *) fc_clause)->args;
	else if (IsA(fc_clause, Const) &&
			 !((Const *) fc_clause)->constisnull &&
			 DatumGetBool(((Const *) fc_clause)->constvalue))
		return NIL;				/* 常量 TRUE 输入 -> NIL 列 */
	else
		return list_make1(fc_clause);
}

/*
 * makeIndexInfo
 *	  创建一个 IndexInfo 节点
 */
IndexInfo * makeIndexInfo(int fc_numattrs, int fc_numkeyattrs, Oid fc_amoid, List *fc_expressions,
			  List *fc_predicates, bool fc_unique, bool fc_nulls_not_distinct, bool fc_isready, bool fc_concurrent)
{
	IndexInfo  *fc_n = makeNode(IndexInfo);

	fc_n->ii_NumIndexAttrs = fc_numattrs;
	fc_n->ii_NumIndexKeyAttrs = fc_numkeyattrs;
	Assert(fc_n->ii_NumIndexKeyAttrs != 0);
	Assert(fc_n->ii_NumIndexKeyAttrs <= fc_n->ii_NumIndexAttrs);
	fc_n->ii_Unique = fc_unique;
	fc_n->ii_NullsNotDistinct = fc_nulls_not_distinct;
	fc_n->ii_ReadyForInserts = fc_isready;
	fc_n->ii_CheckedUnchanged = false;
	fc_n->ii_IndexUnchanged = false;
	fc_n->ii_Concurrent = fc_concurrent;

	/* 表达式 */
	fc_n->ii_Expressions = fc_expressions;
	fc_n->ii_ExpressionsState = NIL;

	/* 谓词  */
	fc_n->ii_Predicate = fc_predicates;
	fc_n->ii_PredicateState = NULL;

	/* 排除约束 */
	fc_n->ii_ExclusionOps = NULL;
	fc_n->ii_ExclusionProcs = NULL;
	fc_n->ii_ExclusionStrats = NULL;

	/* 操作类选项 */
	fc_n->ii_OpclassOptions = NULL;

	/* 投机插入 */
	fc_n->ii_UniqueOps = NULL;
	fc_n->ii_UniqueProcs = NULL;
	fc_n->ii_UniqueStrats = NULL;

	/* 将索引构建状态初始化为默认值 */
	fc_n->ii_BrokenHotChain = false;
	fc_n->ii_ParallelWorkers = 0;

	/* 为索引 AM 的可能使用做好准备 */
	fc_n->ii_Am = fc_amoid;
	fc_n->ii_AmCache = NULL;
	fc_n->ii_Context = CurrentMemoryContext;

	return fc_n;
}

/*
 * makeGroupingSet
 *
 */
GroupingSet *
makeGroupingSet(GroupingSetKind fc_kind, List *fc_content, int fc_location)
{
	GroupingSet *fc_n = makeNode(GroupingSet);

	fc_n->kind = fc_kind;
	fc_n->content = fc_content;
	fc_n->location = fc_location;
	return fc_n;
}

/*
 * makeVacuumRelation -
 *	  创建一个 VacuumRelation 节点
 */
VacuumRelation *
makeVacuumRelation(RangeVar *fc_relation, Oid fc_oid, List *fc_va_cols)
{
	VacuumRelation *fc_v = makeNode(VacuumRelation);

	fc_v->relation = fc_relation;
	fc_v->oid = fc_oid;
	fc_v->va_cols = fc_va_cols;
	return fc_v;
}
